/*
**  This program is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**
************  File Info  ********************************
**
**  Author: CMlr<zxyzxy12321@gmail.com>
**  Date:
**  Project: MyPowder
**  Description:
**
**
*/

#include "common.h"
#include "particles.h"
#include "element.h"
#include "graphic.h"
#include "defines.h"

#define DOWNWAY_NOWAY 0x0
#define DOWNWAY_LEFT  0x1
#define DOWNWAY_RIGHT 0x2


void init_part(part_t* part, int x, int y, int type)
{
    part->type = type;
    part->ctype = PT_NONE;
    part->x = x;
    part->y = y;
    part->life = elem[type].life;
    part->temp = elem[type].temp;
    part->tmp = 0;
}

void create_part(int x, int y, int type)
{
    int i;


    if (pmap[y][x].i != PART_I_INVALID) {
        return;
    }

    for (i = 0; i != MAX_PART_COUNT; ++i) {
        if (parts[i].type == PT_NONE) {
            init_part(&parts[i], x, y, type);
            pmap[y][x].i = i;
            return;
        }
    }
}

void delete_part(int i)
{
    pmap[parts[i].y][parts[i].x].i = PART_I_INVALID;
    init_part(&parts[i], -1, -1, PT_NONE);
}


void move_part(int i, int x, int y)
{
    if (out_of_range(x, y)) {
        delete_part(i);
        return;
    }

    pmap[parts[i].y][parts[i].x].i = PART_I_INVALID;
    parts[i].x = x;
    parts[i].y = y;
    pmap[y][x].i = i;
}

int get_part(int x, int y)
{
    if (x < 0)
        x = 0;
    else if (x >= XRES)
        x = XRES - 1;

    if (y < 0)
        y = 0;
    else if (y >= YRES)
        y = YRES - 1;

    return pmap[y][x].i;
}

void gravity_falldown(int i)
{
    int falldown = elem[parts[i].type].gravity;
    int newx = parts[i].x,
        newy = parts[i].y;
    int downway = DOWNWAY_NOWAY;
    int tmp = 0;
    int rx = 0, ry = 0;

    switch (elem[parts[i].type].type) {
    case TYPE_NONE:
        break;
    case TYPE_SOLID:
        break;
    case TYPE_POWDER:
        /* fall through */
    case TYPE_LIQUID:
        if (!falldown) {
            break;
        }
        if (get_part(newx, newy + falldown) == PART_I_INVALID) {
            newy += falldown;
            if (!(rand() % 50)) {
                newy += rand() % 2;
                newy -= rand() % 2;
            }
        } else if (get_part(newx, newy + falldown) == elem[parts[i].type].type) {
            break;
        } else {
            if (get_part(newx - 1, newy + falldown) == PART_I_INVALID) {
                downway |= DOWNWAY_LEFT;
            }
            if (get_part(newx + 1, newy + falldown) == PART_I_INVALID) {
                downway |= DOWNWAY_RIGHT;
            }
            if (downway != DOWNWAY_NOWAY) {
                newy += falldown;

                if ((downway & DOWNWAY_LEFT) && (downway & DOWNWAY_RIGHT)) {
                    newx += rand() % 2 ? -1 : +1;
                } else {
                    newx += downway & DOWNWAY_LEFT ? -1 : +1;
                }
            }
        }
        break;
    case TYPE_GAS:
        for (; ; ) {
            rx = rand() % (falldown + 1) - rand() % (falldown + 1);
            ry = rand() % (falldown + 1) - rand() % (falldown + 1);

            if (get_part(newx + rx, newy + ry) == PART_I_INVALID) {
                newy += ry;
                newx += rx;
                break;
            }
            if (++tmp > 8) {
                break;
            }

        }
        break;
    case TYPE_RASING:
        if (!falldown) {
            break;
        }
        if (get_part(newx, newy - falldown) == PART_I_INVALID) {
            newy -= falldown;
        } else if (get_part(newx, newy - falldown) == elem[parts[i].type].type) {
            break;
        } else {
            if (get_part(newx - 1, newy - falldown) == PART_I_INVALID) {
                downway |= DOWNWAY_LEFT;
            }
            if (get_part(newx + 1, newy - falldown) == PART_I_INVALID) {
                downway |= DOWNWAY_RIGHT;
            }
            if (downway != DOWNWAY_NOWAY) {
                newy -= falldown;

                if ((downway & DOWNWAY_LEFT) && (downway & DOWNWAY_RIGHT)) {
                    newx += rand() % 2 ? -1 : +1;
                } else {
                    newx += downway & DOWNWAY_LEFT ? -1 : +1;
                }
            }
        }
        break;
    default:;
    }

    move_part(i, newx, newy);

//    if (out_of_range(parts[i].x, parts[i].y)) {
//        delete_part(i);
//    }
}


void random_move(int i)
{
    int weight = elem[parts[i].type].weight;
    int newx = parts[i].x,
        newy = parts[i].y;

    switch (elem[parts[i].type].type) {
    case TYPE_NONE:
        break;
    case TYPE_SOLID:
        break;
    case TYPE_LIQUID:
        if (get_part(newx, newy + elem[parts[i].type].gravity) == PART_I_INVALID) {
            break;
        }
        if (rand() % 10 == 0) {
            newx += rand() % 2;
            newx -= rand() % 2;
        } else {
            //newx += rand() % 2;
            //newx -= rand() % 2;
        }
        if (get_part(newx, newy) != PART_I_INVALID) {
            newx = parts[i].x,
            newy = parts[i].y;
        }
        break;
    default:
        break;
    }
    move_part(i, newx, newy);
}

void decrease_life(int i)
{
    parts[i].life -= elem[parts[i].type].lifedec * (rand() % 3 / 3);
}


void clean_dead(int i)
{
    if (parts[i].type == PT_NONE) {
        return;
    }

    if ((parts[i].type < PT_NONE) && (parts[i].type >= PT_MAX)) {
        delete_part(i);
    } else if (parts[i].life <= 0) {
        delete_part(i);
    } else if (out_of_range(parts[i].x, parts[i].y)) {
        delete_part(i);
    }
}

void update_particle(int i)
{
    on_update_t update_func = elem[parts[i].type].on_update;

    if (!update_func) {
        update_func = &default_update;
    }

    if (update_func(i) != 0) {
        delete_part(i);
    }
}

void draw_particle(int i)
{
    on_draw_t draw_func = elem[parts[i].type].on_draw;

    if (!draw_func) {
        draw_func = &default_draw;
    }

    draw_func(i);
}


void init_pmap()
{
    int x, y;

    for (x = 0; x != XRES; ++x) {
        for (y = 0; y != YRES; ++y) {
            pmap[y][x].i = PART_I_INVALID;
        }
    }
}

void init_parts()
{
    int i;
    for (i = 0; i != MAX_PART_COUNT; ++i) {
        //memset(&parts[i], 0, sizeof(part_t));
        parts[i].type = PT_NONE;
    }
}

void global_update()
{
    int i;

    for (i = 0; i != MAX_PART_COUNT; ++i) {
        if (parts[i].type != PT_NONE) {
            clean_dead(i);
            gravity_falldown(i);
            random_move(i);
            decrease_life(i);
            update_particle(i);
        }
    }
}




void draw_all()
{
    int i;

    for (i = 0; i != MAX_PART_COUNT; ++i) {
        if (parts[i].type != PT_NONE) {
            draw_particle(i);
        }
    }
}


int default_update(int i)
{
    return 0;
}

void default_draw(int i)
{
    color_t color = elem[parts[i].type].color;
    draw_pixel(parts[i].x, parts[i].y, color);
}
